<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ZooKeeper 分布式协调服务深度解析 | 技术小馆</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css">
    <link rel="stylesheet" href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.6;
        }
        .hero {
            background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
        }
        .prose h1, .prose h2, .prose h3, .prose h4 {
            font-family: 'Noto Serif SC', serif;
            font-weight: 600;
            color: #2d3748;
        }
        .prose a {
            color: #2b6cb0;
            text-decoration: none;
            transition: all 0.2s ease;
        }
        .prose a:hover {
            color: #1a365d;
            text-decoration: underline;
        }
        .card {
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
        }
        .highlight {
            position: relative;
        }
        .highlight:before {
            content: "";
            position: absolute;
            left: 0;
            bottom: 0;
            width: 100%;
            height: 30%;
            background-color: rgba(56, 178, 172, 0.2);
            z-index: -1;
            transform: scaleX(1.05);
        }
        .code-block {
            background-color: #2d3748;
            color: #f7fafc;
            border-radius: 0.5rem;
            overflow-x: auto;
        }
        .mermaid {
            background-color: white;
            padding: 1rem;
            border-radius: 0.5rem;
            margin: 1.5rem 0;
        }
        .timeline-item:before {
            content: "";
            position: absolute;
            left: -1.5rem;
            top: 0.75rem;
            width: 0.75rem;
            height: 0.75rem;
            border-radius: 50%;
            background-color: #4c51bf;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <header class="hero text-white py-20 px-4 sm:px-6 lg:px-8">
        <div class="max-w-5xl mx-auto text-center">
            <h1 class="text-4xl md:text-5xl font-bold mb-6">ZooKeeper 分布式协调服务深度解析</h1>
            <p class="text-xl md:text-2xl opacity-90 max-w-3xl mx-auto mb-8">构建高可用分布式系统的核心组件</p>
            <div class="flex justify-center space-x-4">
                <a href="#intro" class="bg-white text-blue-800 hover:bg-blue-100 px-6 py-3 rounded-lg font-medium transition duration-300">
                    开始探索
                </a>
                <a href="#applications" class="border-2 border-white hover:bg-white hover:text-blue-800 px-6 py-3 rounded-lg font-medium transition duration-300">
                    应用场景
                </a>
            </div>
        </div>
    </header>

    <!-- Main Content -->
    <main class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <!-- Introduction Section -->
        <section id="intro" class="prose prose-lg max-w-none mb-16">
            <div class="text-center mb-12">
                <h2 class="text-3xl font-bold mb-4">分布式系统面临的挑战</h2>
                <p class="text-gray-600 max-w-3xl mx-auto">在分布式环境下，如果舍弃SpringCloud，如何实现注册中心、配置管理、集群管理、分布式锁等功能？ZooKeeper提供了完美的解决方案。</p>
            </div>

            <div class="grid md:grid-cols-2 gap-8 mb-16">
                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-blue-100 p-3 rounded-full mr-4">
                            <i class="fas fa-question-circle text-blue-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">核心问题</h3>
                    </div>
                    <ul class="space-y-3 text-gray-700">
                        <li class="flex items-start">
                            <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                            <span>如何保证对同一资源访问的数据强一致性？</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                            <span>集群中的Master挂了，如何快速恢复？</span>
                        </li>
                    </ul>
                </div>

                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-purple-100 p-3 rounded-full mr-4">
                            <i class="fas fa-lightbulb text-purple-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">ZooKeeper解决方案</h3>
                    </div>
                    <p class="text-gray-700 mb-4">ZooKeeper 是一个分布式协调服务，为分布式系统提供了高效、可靠的协调和同步机制。</p>
                    <div class="bg-blue-50 p-4 rounded-lg">
                        <p class="font-medium text-blue-800">ZooKeeper = 文件系统 + 监听通知机制</p>
                    </div>
                </div>
            </div>

            <div class="relative rounded-xl overflow-hidden mb-12">
                <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374263961-220a9277-9223-49b6-b8a8-9f4b621174ad.png" 
                     alt="分布式系统示意图" 
                     class="w-full h-auto object-cover">
                <div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div>
                <div class="absolute bottom-0 left-0 p-6 text-white">
                    <h3 class="text-xl font-semibold">分布式系统架构示意图</h3>
                </div>
            </div>
        </section>

        <!-- Installation Section -->
        <section id="installation" class="mb-16">
            <h2 class="text-3xl font-bold mb-8 text-center">ZooKeeper 安装</h2>
            <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                <div class="bg-gray-800 px-4 py-3 flex items-center">
                    <div class="flex space-x-2 mr-4">
                        <div class="w-3 h-3 rounded-full bg-red-500"></div>
                        <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                        <div class="w-3 h-3 rounded-full bg-green-500"></div>
                    </div>
                    <p class="text-gray-300 text-sm">docker-compose.yml</p>
                </div>
                <div class="code-block p-4">
                    <pre class="overflow-x-auto"><code class="language-yaml">version: "3.1"
services:
  zk:
    image: daocloud.io/daocloud/zookeeper:latest
    restart: always
    container_name: zk
    ports:
      - 2181:2181</code></pre>
                </div>
            </div>
        </section>

        <!-- Architecture Section -->
        <section id="architecture" class="mb-16">
            <h2 class="text-3xl font-bold mb-8 text-center">ZooKeeper 架构</h2>
            
            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">树形结构</h3>
                <div class="grid md:grid-cols-2 gap-8 items-center">
                    <div>
                        <p>ZooKeeper 的树形结构是由 znode 组成的，类似于文件系统的目录结构。树的根节点被称为 "/"（斜杠），而每个 znode 都有一个唯一的路径标识符。</p>
                        <p class="mt-4">在 ZooKeeper 的树形结构中，每个 znode 都可以包含数据和子节点。子节点可以是持久节点、临时节点、持久顺序节点或临时顺序节点中的一种类型。</p>
                    </div>
                    <div class="bg-white p-4 rounded-lg shadow-md">
                        <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374264810-95b1701f-5101-436b-bdca-84967df1b380.png" 
                             alt="ZooKeeper树形结构" 
                             class="w-full h-auto rounded-md">
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">Znode类型</h3>
                <div class="grid gap-6 md:grid-cols-2">
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-blue-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-database"></i>
                            </div>
                            <h4 class="text-lg font-semibold">持久节点（Persistent）</h4>
                        </div>
                        <p class="text-gray-700">创建后即持久存在于 ZooKeeper 中，除非主动删除。</p>
                    </div>
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-green-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-clock"></i>
                            </div>
                            <h4 class="text-lg font-semibold">临时节点（Ephemeral）</h4>
                        </div>
                        <p class="text-gray-700">与客户端会话关联，客户端会话结束或失效后自动删除。</p>
                    </div>
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-purple-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-sort-numeric-down"></i>
                            </div>
                            <h4 class="text-lg font-semibold">持久顺序节点（Persistent Sequential）</h4>
                        </div>
                        <p class="text-gray-700">与持久节点类似，但具有顺序号，ZooKeeper 会为每个节点名称自动追加一个序号。</p>
                    </div>
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-yellow-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-sort-numeric-down"></i>
                            </div>
                            <h4 class="text-lg font-semibold">临时顺序节点（Ephemeral Sequential）</h4>
                        </div>
                        <p class="text-gray-700">与临时节点类似，具有顺序号，与客户端会话关联。</p>
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">监听通知机制</h3>
                <div class="grid md:grid-cols-2 gap-8 items-center">
                    <div class="bg-white p-4 rounded-lg shadow-md">
                        <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374265105-1600fa3d-2a89-4290-8e15-edf98a446016.png" 
                             alt="监听通知机制" 
                             class="w-full h-auto rounded-md">
                    </div>
                    <div>
                        <p>ZooKeeper提供了节点watch的功能，客户端监控ZooKeeper上的节点，当节点变动的时候，客户端会收到变动事件和变动后的内容。</p>
                        <p class="mt-4">基于这个特性，我们可以给服务器集群中的所有机器都注册watch事件，监控特定znode。需要更新代码的时候，修改znode中的值，服务器集群中的每一台server都会收到代码更新事件，然后触发调用，更新目标代码。</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Operations Section -->
        <section id="operations" class="mb-16">
            <h2 class="text-3xl font-bold mb-8 text-center">ZooKeeper 常用操作</h2>
            
            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">常用命令</h3>
                <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card">
                    <div class="bg-gray-800 px-4 py-3 flex items-center">
                        <div class="flex space-x-2 mr-4">
                            <div class="w-3 h-3 rounded-full bg-red-500"></div>
                            <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                            <div class="w-3 h-3 rounded-full bg-green-500"></div>
                        </div>
                        <p class="text-gray-300 text-sm">ZooKeeper CLI</p>
                    </div>
                    <div class="code-block p-4">
                        <pre class="overflow-x-auto"><code class="language-bash"># 连接 ZooKeeper 服务器
zkCli.sh -server &lt;ZooKeeper服务器IP&gt;:&lt;端口号&gt;

# 查看节点
ls /path

# 创建节点
create /path data

# 获取节点数据
get /path

# 设置节点数据
set /path data

# 删除节点
delete /path

# 查看节点详情
stat /path

# 退出客户端
quit</code></pre>
                    </div>
                </div>
                <div class="bg-white p-4 rounded-lg shadow-md">
                    <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374265442-cb4b83db-2cf4-4ff6-9c9b-0eae83597f6b.png" 
                         alt="ZooKeeper命令示例" 
                         class="w-full h-auto rounded-md">
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">Java 客户端操作</h3>
                <div class="grid md:grid-cols-2 gap-8">
                    <div>
                        <h4 class="text-lg font-semibold mb-4">Maven 依赖</h4>
                        <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                            <div class="bg-gray-800 px-4 py-3 flex items-center">
                                <div class="flex space-x-2 mr-4">
                                    <div class="w-3 h-3 rounded-full bg-red-500"></div>
                                    <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                                    <div class="w-3 h-3 rounded-full bg-green-500"></div>
                                </div>
                                <p class="text-gray-300 text-sm">pom.xml</p>
                            </div>
                            <div class="code-block p-4">
                                <pre class="overflow-x-auto"><code class="language-xml">&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.zookeeper&lt;/groupId&gt;
        &lt;artifactId&gt;zookeeper&lt;/artifactId&gt;
        &lt;version&gt;3.6.0&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.curator&lt;/groupId&gt;
        &lt;artifactId&gt;curator-recipes&lt;/artifactId&gt;
        &lt;version&gt;2.12.0&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;junit&lt;/groupId&gt;
        &lt;artifactId&gt;junit&lt;/artifactId&gt;
        &lt;version&gt;4.12&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code></pre>
                            </div>
                        </div>
                    </div>
                    <div>
                        <h4 class="text-lg font-semibold mb-4">连接工具类</h4>
                        <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                            <div class="bg-gray-800 px-4 py-3 flex items-center">
                                <div class="flex space-x-2 mr-4">
                                    <div class="w-3 h-3 rounded-full bg-red-500"></div>
                                    <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                                    <div class="w-3 h-3 rounded-full bg-green-500"></div>
                                </div>
                                <p class="text-gray-300 text-sm">ZkUtil.java</p>
                            </div>
                            <div class="code-block p-4">
                                <pre class="overflow-x-auto"><code class="language-java">public class ZkUtil {
    public static CuratorFramework cf(){
        // 超时时间是3s，重试2次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("192.168.193.88:2181")
                .retryPolicy(retryPolicy)
                .build();

        cf.start();
        return cf;
    }
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">基本操作示例</h3>
                <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                    <div class="bg-gray-800 px-4 py-3 flex items-center">
                        <div class="flex space-x-2 mr-4">
                            <div class="w-3 h-3 rounded-full bg-red-500"></div>
                            <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                            <div class="w-3 h-3 rounded-full bg-green-500"></div>
                        </div>
                        <p class="text-gray-300 text-sm">Demo2.java</p>
                    </div>
                    <div class="code-block p-4">
                        <pre class="overflow-x-auto"><code class="language-java">public class Demo2 {
    CuratorFramework cf = ZkUtil.cf();

    // 获取子节点
    @Test
    public void getChildren() throws Exception {
        List&lt;String&gt; strings = cf.getChildren().forPath("/");
        for (String string : strings) {
            System.out.println(string);
        }
    }

    // 获取节点数据
    @Test
    public void getData() throws Exception {
        byte[] bytes = cf.getData().forPath("/qf");
        System.out.println(new String(bytes,"UTF-8"));
    }

    // 添加节点
    @Test
    public void create() throws Exception {
        cf.create().withMode(CreateMode.PERSISTENT)
           .forPath("/qf2","uuuu".getBytes());
    }
    
    // 修改节点
    @Test
    public void update() throws Exception {
        cf.setData().forPath("/qf2","oooo".getBytes());
    }
    
    // 删除节点
    @Test
    public void delete() throws Exception {
        cf.delete().deletingChildrenIfNeeded().forPath("/qf2");
    }
    
    // 查看节点状态
    @Test
    public void stat() throws Exception {
        Stat stat = cf.checkExists().forPath("/qf");
        System.out.println(stat);
    }
}</code></pre>
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">监听通知机制实现</h3>
                <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                    <div class="bg-gray-800 px-4 py-3 flex items-center">
                        <div class="flex space-x-2 mr-4">
                            <div class="w-3 h-3 rounded-full bg-red-500"></div>
                            <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                            <div class="w-3 h-3 rounded-full bg-green-500"></div>
                        </div>
                        <p class="text-gray-300 text-sm">Demo3.java</p>
                    </div>
                    <div class="code-block p-4">
                        <pre class="overflow-x-auto"><code class="language-java">public class Demo3 {
    CuratorFramework cf = ZkUtil.cf();

    @Test
    public void tesetListener() throws Exception {
        TreeCache nodeCache = new TreeCache(cf,"/demo");

        nodeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) {
                ChildData eventData = event.getData();
                switch (event.getType()) {
                    case NODE_ADDED:
                        System.out.println("/demo 节点添加" + eventData.getPath() + 
                             "\t添加数据为：" + new String(eventData.getData()));
                        break;
                    case NODE_UPDATED:
                        System.out.println(eventData.getPath() + "节点数据更新\t更新数据为：" + 
                             new String(eventData.getData()) + "\t版本为：" + eventData.getStat().getVersion());
                        break;
                    case NODE_REMOVED:
                        System.out.println(eventData.getPath() + "节点被删除");
                        break;
                    default:
                        break;
                }
            }
        });
        
        nodeCache.start();
        System.out.println("监听开始。。。");
        System.in.read();
    }
}</code></pre>
                    </div>
                </div>
            </div>
        </section>

        <!-- Cluster Section -->
        <section id="cluster" class="mb-16">
            <h2 class="text-3xl font-bold mb-8 text-center">ZooKeeper 集群</h2>
            
            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">集群架构</h3>
                <div class="bg-white p-4 rounded-lg shadow-md mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374265752-6127fc73-73d5-49f7-b0b5-819d4254c667.png" 
                         alt="ZooKeeper集群架构" 
                         class="w-full h-auto rounded-md">
                </div>
                
                <div class="grid md:grid-cols-3 gap-6 mb-8">
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-red-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-crown"></i>
                            </div>
                            <h4 class="text-lg font-semibold">Leader (Master)</h4>
                        </div>
                        <p class="text-gray-700">事务请求的唯一处理者，也可以处理读请求。</p>
                    </div>
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-blue-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-users"></i>
                            </div>
                            <h4 class="text-lg font-semibold">Follower (Slave)</h4>
                        </div>
                        <p class="text-gray-700">可以直接处理客户端的读请求，向其响应；将事务请求转发给Leader处理。</p>
                    </div>
                    <div class="bg-white p-6 rounded-xl shadow-md card">
                        <div class="flex items-center mb-4">
                            <div class="bg-purple-500 text-white p-2 rounded-lg mr-4">
                                <i class="fas fa-eye"></i>
                            </div>
                            <h4 class="text-lg font-semibold">Observer (Slave)</h4>
                        </div>
                        <p class="text-gray-700">不参与Leader选举，协助Follower处理更多读请求，提高读吞吐量。</p>
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">数据同步机制</h3>
                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <ol class="list-decimal pl-6 space-y-3 text-gray-700">
                        <li>如果有机器要对节点做更新，这个机器先告诉Leader。</li>
                        <li>Leader收到后请求后广播给所有的节点进行写操作。</li>
                        <li>每个机器写完后都给Leader汇报是否写入成功。</li>
                        <li>如果有一半的机器写成功了Leader就下发第二个指令。</li>
                        <li>提交事务，以广播的形式发出。</li>
                    </ol>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">Leader 选举</h3>
                <div class="grid md:grid-cols-2 gap-8">
                    <div>
                        <div class="bg-white p-6 rounded-xl shadow-md card mb-6">
                            <h4 class="text-lg font-semibold mb-3">选举规则</h4>
                            <ul class="space-y-2">
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                                    <span>每个ZooKeeper服务都有一个全局唯一的myid</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                                    <span>每个写操作都有一个全局唯一的zxid，数据越新zxid越大</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                                    <span>选举出zxid最大的节点作为Leader</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-green-500 mr-2 mt-1"></i>
                                    <span>在zxid相同的节点中，选举myid最大的节点作为Leader</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                    <div>
                        <div class="bg-white p-6 rounded-xl shadow-md card">
                            <h4 class="text-lg font-semibold mb-3">端口说明</h4>
                            <ul class="space-y-2">
                                <li class="flex items-start">
                                    <span class="font-medium w-24">2181</span>
                                    <span>对客户端提供服务</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="font-medium w-24">3888</span>
                                    <span>选举Leader使用</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="font-medium w-24">2888</span>
                                    <span>集群内机器通讯使用（Leader监听此端口）</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">集群搭建</h3>
                <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                    <div class="bg-gray-800 px-4 py-3 flex items-center">
                        <div class="flex space-x-2 mr-4">
                            <div class="w-3 h-3 rounded-full bg-red-500"></div>
                            <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                            <div class="w-3 h-3 rounded-full bg-green-500"></div>
                        </div>
                        <p class="text-gray-300 text-sm">docker-compose.yml</p>
                    </div>
                    <div class="code-block p-4">
                        <pre class="overflow-x-auto"><code class="language-yaml">version: "3.1"
services:
  zk1:
    image: zookeeper
    restart: always
    container_name: zk1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
  zk2:
    image: zookeeper
    restart: always
    container_name: zk2
    ports:
      - 2182:2181
    environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
  zk3:
    image: zookeeper
    restart: always
    container_name: zk3
    ports:
      - 2183:2181
    environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181</code></pre>
                    </div>
                </div>
                <div class="mt-4 text-sm text-gray-600">
                    <p>查看节点状态命令：<code class="bg-gray-100 px-2 py-1 rounded">zkServer.sh status</code></p>
                </div>
            </div>

            <div class="mb-12">
                <h3 class="text-2xl font-semibold mb-6 highlight">过半数存活原则</h3>
                <div class="grid md:grid-cols-2 gap-8">
                    <div>
                        <p>在ZooKeeper集群中，当存活的机器数量超过总机器的一半时，整个集群才能正常工作，否则拒绝访问。</p>
                        <p class="mt-4">基于过半数存活原则，ZooKeeper的集群机器数量一定是奇数台，因为2N+1和2N+2的容灾能力是一样的，基于成本考虑2N+1台的选择方案更优。</p>
                    </div>
                    <div class="bg-white p-4 rounded-lg shadow-md">
                        <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374266068-7c86224b-450f-4d16-9714-d39194012e23.png" 
                             alt="脑裂问题示意图" 
                             class="w-full h-auto rounded-md">
                    </div>
                </div>
                <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mt-6">
                    <div class="flex">
                        <div class="flex-shrink-0">
                            <i class="fas fa-exclamation-triangle text-yellow-400"></i>
                        </div>
                        <div class="ml-3">
                            <h4 class="text-sm font-medium text-yellow-800">为什么需要过半数存活机制？</h4>
                            <p class="mt-1 text-sm text-yellow-700">为了防止网络脑裂，保证数据的强一致性。因为整个集群中，有可能因为网络问题"脑裂"，导致整个集群分为2个甚至多个集群。如果没有过半数存活机制，那么整个ZooKeeper会变成多个集群，无法保证数据一致性。</p>
                        </div>
                    </div>
                </div>
            </div>

            <div>
                <h3 class="text-2xl font-semibold mb-6 highlight">Java连接ZooKeeper集群</h3>
                <div class="bg-white rounded-xl shadow-md overflow-hidden card">
                    <div class="bg-gray-800 px-4 py-3 flex items-center">
                        <div class="flex space-x-2 mr-4">
                            <div class="w-3 h-3 rounded-full bg-red-500"></div>
                            <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                            <div class="w-3 h-3 rounded-full bg-green-500"></div>
                        </div>
                        <p class="text-gray-300 text-sm">ZkUtil.java</p>
                    </div>
                    <div class="code-block p-4">
                        <pre class="overflow-x-auto"><code class="language-java">public class ZkUtil {
    public static CuratorFramework cf(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183")
                .retryPolicy(retryPolicy)
                .build();

        cf.start();
        return cf;
    }
}</code></pre>
                    </div>
                </div>
                <div class="mt-6 bg-white p-4 rounded-lg shadow-md">
                    <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374266388-c3b5d98a-57cc-4a46-93fc-5b97f225b06c.png" 
                         alt="测试类示例" 
                         class="w-full h-auto rounded-md">
                </div>
            </div>
        </section>

        <!-- Applications Section -->
        <section id="applications" class="mb-16">
            <h2 class="text-3xl font-bold mb-8 text-center">ZooKeeper 应用场景</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-12">
                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-blue-100 p-3 rounded-full mr-4">
                            <i class="fas fa-cog text-blue-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">配置文件管理</h3>
                    </div>
                    <div class="bg-white p-4 rounded-lg shadow-md mb-4">
                        <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374266692-295cecc9-7512-488d-a645-f8229babff86.png" 
                             alt="配置文件管理" 
                             class="w-full h-auto rounded-md">
                    </div>
                    <p class="text-gray-700">将需要统一管理的配置全部放到ZooKeeper上去，保存在某个目录节点中。所有相关应用程序对这个目录节点进行监听，一旦配置信息发生变化，每个应用程序就会收到通知，然后从ZooKeeper获取新的配置信息应用到系统中。</p>
                </div>

                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-purple-100 p-3 rounded-full mr-4">
                            <i class="fas fa-server text-purple-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">集群管理</h3>
                    </div>
                    <p class="text-gray-700 mb-4">所有机器约定在父目录GroupMembers下创建临时目录节点，然后监听父目录节点的子节点变化消息。一旦有机器挂掉，该机器与ZooKeeper的连接断开，其所创建的临时目录节点被删除，所有其他机器都收到通知。</p>
                    <p class="text-gray-700">新机器加入也是类似，所有机器创建临时顺序编号目录节点，每次选取编号最小的机器作为master。</p>
                </div>

                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-green-100 p-3 rounded-full mr-4">
                            <i class="fas fa-lock text-green-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">分布式锁</h3>
                    </div>
                    <p class="text-gray-700">将ZooKeeper上的一个znode看作是一把锁，通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点，最终成功创建的那个客户端即拥有了这把锁。用完删除掉自己创建的distribute_lock节点就释放出锁。</p>
                </div>

                <div class="bg-white p-6 rounded-xl shadow-md card">
                    <div class="flex items-center mb-4">
                        <div class="bg-yellow-100 p-3 rounded-full mr-4">
                            <i class="fas fa-signature text-yellow-600 text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">命名服务</h3>
                    </div>
                    <p class="text-gray-700 mb-2">(Dubbo监控中心原理)</p>
                    <p class="text-gray-700">服务B部署成功后，可以先到ZooKeeper注册服务（添加节点/service/B和节点数据）。服务A监控ZooKeeper服务节点/service/B，如果发现节点有数据了，那么服务A就可以访问服务B了。</p>
                </div>
            </div>

            <div class="bg-white p-6 rounded-xl shadow-md card">
                <div class="flex items-center mb-4">
                    <div class="bg-red-100 p-3 rounded-full mr-4">
                        <i class="fas fa-search text-red-600 text-xl"></i>
                    </div>
                    <h3 class="text-xl font-semibold">发现服务</h3>
                </div>
                <div class="bg-white p-4 rounded-lg shadow-md mb-4">
                    <img src="https://cdn.nlark.com/yuque/0/2023/png/21449790/1688374267039-a0883567-594e-4404-99f5-4489ef29ddd2.png" 
                         alt="发现服务" 
                         class="w-full h-auto rounded-md">
                </div>
                <p class="text-gray-700">注册一个持久节点/service/business/what，他下面的每个子节点都是一个可用服务，保存了服务的地址端口等信息。服务调用者通过ZooKeeper获取/service/business/what所有子节点信息来得到可用的服务。</p>
                <p class="text-gray-700 mt-4">下面的节点都是临时节点，服务器启动的时候会过来注册一个临时节点，服务器挂掉之后或主动关闭之后，临时节点会自动移除，这样就可以保证使用者获取的what服务都是可用的，而且可以动态的扩容缩容。</p>
            </div>
        </section>
    </main>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-8 px-4 sm:px-6 lg:px-8">
        <div class="max-w-6xl mx-auto">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-xl font-semibold text-white mb-2">技术小馆</h3>
                    <a href="http://www.yuque.com/jtostring" class="hover:text-white transition duration-300">
                        <i class="fas fa-globe mr-2"></i>http://www.yuque.com/jtostring
                    </a>
                </div>
                <div class="flex space-x-6">
                    <a href="#" class="hover:text-white transition duration-300">
                        <i class="fab fa-github text-xl"></i>
                    </a>
                    <a href="#" class="hover:text-white transition duration-300">
                        <i class="fab fa-twitter text-xl"></i>
                    </a>
                    <a href="#" class="hover:text-white transition duration-300">
                        <i class="fab fa-weixin text-xl"></i>
                    </a>
                </div>
            </div>
            <div class="border-t border-gray-800 mt-8 pt-8 text-sm text-center text-gray-500">
                © 2023 技术小馆. All rights reserved.
            </div>
        </div>
    </footer>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>